package com.flyqiu.flow.core.manage;

import com.alibaba.fastjson2.JSONObject;
import com.flyqiu.common.clazz.FlyQiuClassTools;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import com.flyqiu.common.tools.MapTools;
import com.flyqiu.flow.event.api.FlyQiuFlowEventManage;
import com.flyqiu.flow.event.base.InstCompleteEvent;
import com.flyqiu.flow.event.base.InstCreateEvent;
import com.flyqiu.flow.event.base.InstDeleteEvent;
import com.flyqiu.flow.event.base.ProcEvent;

import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 流程全局事件管理
 */
public class DefaultFlyQiuFlowEventManage implements FlyQiuFlowEventManage {

    protected final List<ProcEvent> listEvents = new ArrayList<>();
    protected final Map<String, List<InstDeleteEvent>> deleteEventMap = new HashMap<>();
    protected final Map<String, List<InstCompleteEvent>> completeEventMap = new HashMap<>();
    protected final Map<String, List<InstCreateEvent>> createEventMap = new HashMap<>();


    public <T extends ProcEvent<?>> void addEvent(Class<T> clazz, T eventInst) {

        if (listEvents.contains(eventInst)) {
            return;
        }
        listEvents.add(eventInst);
        String clazzName = clazz.getName();
        if (eventInst instanceof InstDeleteEvent) {
            MapTools.mapListAddValue(deleteEventMap, clazzName, eventInst);
        }
        if (eventInst instanceof InstCompleteEvent) {
            MapTools.mapListAddValue(completeEventMap, clazzName, eventInst);
        }
        if (eventInst instanceof InstCreateEvent) {
            MapTools.mapListAddValue(createEventMap, clazzName, eventInst);
        }

    }

    @Override
    public <T extends ProcEvent<?>> void removeEvent(Class<T> clazz) {
        FlyQiuCollectionTools.remove(listEvents, procEvent -> clazz.equals(procEvent.getClass()));
        String key = clazz.getName();
        MapTools.mapRemove(deleteEventMap, key, null);
        MapTools.mapRemove(completeEventMap, key, null);
        MapTools.mapRemove(createEventMap, key, null);
    }

    @Override
    public <T extends ProcEvent<?>> void removeEvent(T eventInst) {
        FlyQiuCollectionTools.remove(listEvents, eventInst::equals);
        FlyQiuCollectionTools.iterator(deleteEventMap, (s, instDeleteEvents) -> FlyQiuCollectionTools.remove(instDeleteEvents, eventInst::equals));
        FlyQiuCollectionTools.iterator(completeEventMap, (s, instDeleteEvents) -> FlyQiuCollectionTools.remove(instDeleteEvents, eventInst::equals));
        FlyQiuCollectionTools.iterator(createEventMap, (s, instDeleteEvents) -> FlyQiuCollectionTools.remove(instDeleteEvents, eventInst::equals));
    }

    @SuppressWarnings("unchecked")
    public <T, I> void triggerCreateBeforeEvent(Class<T> clazz, I inst) {
        MapTools.mapListConsumerItem(createEventMap, clazz.getName(), (Consumer<InstCreateEvent>) event -> event.createBefore(inst));
    }

    @SuppressWarnings("unchecked")
    public <T, I> void triggerCreateAfterEvent(Class<T> clazz, I inst) {
        MapTools.mapListConsumerItem(createEventMap, clazz.getName(), (Consumer<InstCreateEvent>) event -> event.createAfter(inst));
    }

    /**
     * 完成前
     *
     * @param clazz
     * @param inst
     * @param <T>
     * @param <I>
     */
    @SuppressWarnings("unchecked")
    public <T, I> void triggerCompleteBeforeEvent(Class<T> clazz, I inst) {
        MapTools.mapListConsumerItem(completeEventMap, clazz.getName(), (Consumer<InstCompleteEvent>) event -> event.completeBefore(inst));

    }

    /**
     * 完成后
     *
     * @param clazz
     * @param inst
     * @param <T>
     * @param <I>
     */
    @SuppressWarnings("unchecked")
    public <T, I> void triggerCompleteAfterEvent(Class<T> clazz, I inst) {
        MapTools.mapListConsumerItem(completeEventMap, clazz.getName(), (Consumer<InstCompleteEvent>) event -> event.completeAfter(inst));
    }

    @Override
    public <T, I> void triggerDeleteBeforeEvent(Class<T> clazz, I inst, JSONObject params, String reason) {
        MapTools.mapListConsumerItem(deleteEventMap, clazz.getName(), (Consumer<InstDeleteEvent>) event -> event.deleteBefore(inst, params, reason));
    }

    /**
     * 删除后
     *
     * @param clazz  类型
     * @param inst   实例
     * @param params 扩展参数
     * @param reason 删除原因
     */
    @Override
    public <T, I> void triggerDeleteAfterEvent(Class<T> clazz, I inst, JSONObject params, String reason) {
        MapTools.mapListConsumerItem(deleteEventMap, clazz.getName(), (Consumer<InstDeleteEvent>) event -> event.deleteAfter(inst, params, reason));
    }


    @Override
    public <T, I> void triggerCustomEvent(Class<T> clazz, I inst, String methodName, Object... params) {
        Object[] targetMethodParams = getTargetMethodParams(inst, params);
        Class<?>[] paramsTypes = FlyQiuClassTools.getObjTypes(targetMethodParams);
        FlyQiuCollectionTools.iterator(listEvents, procEvent -> {
            if (!clazz.isAssignableFrom(procEvent.getClass())) {
                return;
            }
            Method method = FlyQiuClassTools.findPublicMethod(clazz, methodName, paramsTypes);
            if (method == null) {
                String paramsTypeKey = Arrays.stream(paramsTypes).map(Class::getName).collect(Collectors.joining(","));
                System.out.printf("Could not find method:%s.%s(%s) methodInvoke\n", clazz.getName(), methodName, paramsTypeKey);
                return;
            }
            FlyQiuClassTools.methodInvoke(method, procEvent, targetMethodParams);
        });
    }

    private <I> Object[] getTargetMethodParams(I inst, Object... params) {
        int paramsCount = 0;
        if (params != null) {
            paramsCount = params.length + 1;
        }
        Object[] targetParams = new Object[paramsCount];
        targetParams[0] = inst;
        System.arraycopy(params, 0, targetParams, 1, targetParams.length - 1);
        return targetParams;
    }


}
